package com.ttg.utils.sm;


import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class CryptoUtil {
    /**
     * 对字符串sha1编码
     */
    public static String SHA1(String decript) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            digest.update(decript.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuilder hexString = new StringBuilder();
            // 字节数组转换为 十六进制 数
            for (byte message : messageDigest) {
                String shaHex = Integer.toHexString(message & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * MD5加密
     */
    public static String Md5(String source) throws NoSuchAlgorithmException {
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        byte[] bytes = md5.digest(source.getBytes());
        // 字符数组转换成字符串返回
        return byteArrayToHex(bytes);
    }

    /**
     * 字符数组转换成字符串
     */
    private static String byteArrayToHex(byte[] byteArray) {
        // 首先初始化一个字符数组，用来存放每个16进制字符
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        // new一个字符数组，这个就是用来组成结果字符串的（解释一下：一个byte是八位二进制，也就是2位十六进制字符（2的8次方等于16的2次方））
        char[] resultCharArray = new char[byteArray.length * 2];
        // 遍历字节数组，通过位运算（位运算效率高），转换成字符放到字符数组中去
        int index = 0;
        for (byte b : byteArray) {
            resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];
            resultCharArray[index++] = hexDigits[b & 0xf];
        }
        // 字符数组组合成字符串返回
        return new String(resultCharArray);
    }


    private static String KEY_ALGORITHM = "RSA/ECB/PKCS1Padding";

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /*** RSA最大解密密文大小 */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * RSA公钥解密
     */
    public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey) throws Exception {
        byte[] keyBytes = new BASE64Decoder().decodeBuffer(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, publicK);
        return cipher.doFinal(encryptedData);
    }

    /**
     * RSA公钥加密
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
        byte[] keyBytes = new BASE64Decoder().decodeBuffer(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        return cipher.doFinal(data);
        // Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        // cipher.init(Cipher.ENCRYPT_MODE, publicK);
        // int blockSize = cipher.getBlockSize();//
        // 获得加密块大小，如：加密前数据为128个byte，而key_size=1024
        // // 加密块大小为127byte,加密后为128个byte;因此共有2个加密块，第一个127byte第二个为1个byte
        // int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
        // int leavedSize = data.length % blockSize;
        // int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
        // : data.length / blockSize;
        // byte[] raw = new byte[outputSize * blocksSize];
        // int i = 0;
        // while (data.length - i * blockSize > 0) {
        // if (data.length - i * blockSize > blockSize)
        // cipher.doFinal(data, i * blockSize, blockSize, raw, i
        // * outputSize);
        // else
        // cipher.doFinal(data, i * blockSize, data.length - i
        // * blockSize, raw, i * outputSize);
        // // 这里面doUpdate方法不可用，查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到
        // //
        // ByteArrayOutputStream中，而最后doFinal的时候才将所有的byte[]进行加密，可是到了此时加密块大小很可能已经超出了
        // // OutputSize所以只好用dofinal方法。
        //
        // i++;
        // }
        // return raw;

        // return cipher.doFinal(data);
    }

    /**
     * RSA私钥解密
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
        byte[] keyBytes = new BASE64Decoder().decodeBuffer(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        // Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        return cipher.doFinal(encryptedData);
    }

    /**
     * RSA私钥加密
     */
    public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = new BASE64Decoder().decodeBuffer(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, privateK);
        return cipher.doFinal(data);
    }

    /**
     * 二进制转十六进制
     */
    public static String bin2hex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    /**
     * 十六进制转二进制
     */
    public static byte[] hex2bin(String src) {
        int l = src.length() / 2;
        byte[] ret = new byte[l];
        for (int i = 0; i < l; i++) {
            ret[i] = Integer.valueOf(src.substring(i * 2, i * 2 + 2), 16).byteValue();
        }
        return ret;
    }

    private static String AES_KEY_ALGORITHM = "AES/ECB/PKCS5Padding";

    // private static String DES = "AES";

    /**
     * AES解密
     */
    public static String AESDecrypt(String sSrc, String sKey) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(sKey.getBytes("ASCII"), "AES");
        Cipher cipher = Cipher.getInstance(AES_KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        byte[] encrypted1 = hex2bin(sSrc);
        byte[] original = cipher.doFinal(encrypted1);
        return new String(original);
    }

    /**
     * AES加密
     */
    public static String AESEncrypt(String sSrc, String sKey) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(sKey.getBytes("ASCII"), "AES");
        Cipher cipher = Cipher.getInstance(AES_KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(sSrc.getBytes());
        return bin2hex(encrypted);
    }

    /**
     * AES加密,base64编码
     */
    public static String AESEncryptBase64(String sSrc, String sKey) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(sKey.getBytes("ASCII"), "AES");
        Cipher cipher = Cipher.getInstance(AES_KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(sSrc.getBytes());
        return new BASE64Encoder().encodeBuffer(encrypted);
    }

    /**
     * AES解密,base64编码
     */
    public static String AESDecryptBase64(String sSrc, String sKey) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(sKey.getBytes("ASCII"), "AES");
        Cipher cipher = Cipher.getInstance(AES_KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);
        byte[] original = cipher.doFinal(encrypted1);
        return new String(original);
    }


    /**
     * 私钥签名
     *
     * @param prikeyvalue
     * @param sign_str
     * @return
     */
    public static String sign(String prikeyvalue, String sign_str) {
//        try {
//            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(prikeyvalue));
//            KeyFactory keyf = KeyFactory.getInstance("RSA");
//            PrivateKey myprikey = keyf.generatePrivate(priPKCS8);
//            Signature signet = Signature.getInstance("SHA1withRSA");
//            signet.initSign(myprikey);
//            signet.update(sign_str.getBytes("UTF-8"));
//            byte[] signed = signet.sign();
//            return bin2hex(signed);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return null;
        try{
            return sign(new BASE64Decoder().decodeBuffer(prikeyvalue),sign_str);
        }catch(Exception ex){
            ex.printStackTrace();
        }
        return null;
    }

    public static String sign(byte[] prikeyvalue, String sign_str) {
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(prikeyvalue);
            KeyFactory keyf = KeyFactory.getInstance("RSA");
            PrivateKey myprikey = keyf.generatePrivate(priPKCS8);
            Signature signet = Signature.getInstance("SHA1withRSA");
            signet.initSign(myprikey);
            signet.update(sign_str.getBytes("UTF-8"));
            byte[] signed = signet.sign();
            return new BASE64Encoder().encodeBuffer(signed).replace("\r\n","").replace("\n","");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 验证签名
     *
     * @param pubkeyvalue
     * @param oid_str
     * @param signed_str
     * @return
     */
    public static boolean checksign(String pubkeyvalue, String oid_str, String signed_str) {
        try {
//            X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(new BASE64Decoder().decodeBuffer(pubkeyvalue));
//            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//            PublicKey pubKey = keyFactory.generatePublic(bobPubKeySpec);
//            byte[] signed = hex2bin(signed_str);
//            Signature signetcheck = Signature.getInstance("SHA1withRSA");
//            signetcheck.initVerify(pubKey);
//            signetcheck.update(oid_str.getBytes("UTF-8"));
//            return signetcheck.verify(signed);

            return checksign(new BASE64Decoder().decodeBuffer(pubkeyvalue),oid_str,signed_str);
        } catch (Exception e) {
        }
        return false;
    }

    public static boolean checksign(byte[] pubkeyvalue, String oid_str, String signed_str) {
        try {
            X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(pubkeyvalue);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey pubKey = keyFactory.generatePublic(bobPubKeySpec);
            byte[] signed = new BASE64Decoder().decodeBuffer(signed_str);
            Signature signetcheck = Signature.getInstance("SHA1withRSA");
            signetcheck.initVerify(pubKey);
            signetcheck.update(oid_str.getBytes("UTF-8"));
            return signetcheck.verify(signed);
        } catch (Exception e) {
        }
        return false;
    }

    /**
     * 从文件中输入流中加载公钥
     *
     * @param path 公钥输入流
     * @throws Exception 加载公钥时产生的异常
     */
    public static String loadPublicKeyByFile(String path) throws Exception {
        try {
            BufferedReader br = new BufferedReader(new FileReader(path
            ));
            String readLine = null;
            StringBuilder sb = new StringBuilder();
            while ((readLine = br.readLine()) != null) {
                if (readLine.charAt(0) == '-') {
                    continue;
                } else {
                    sb.append(readLine);
                    sb.append('\r');
                }
            }
            br.close();
            return sb.toString();
        } catch (IOException e) {
            throw new Exception("公钥数据流读取错误");
        } catch (NullPointerException e) {
            throw new Exception("公钥输入流为空");
        }
    }

    /**
     * 从字符串中加载公钥
     *
     * @param publicKeyStr 公钥数据字符串
     * @throws Exception 加载公钥时产生的异常
     */
    public static byte[] loadPublicKeyByStr(String publicKeyStr)
            throws Exception {
        try {
            BASE64Decoder base64 = new BASE64Decoder();
            byte[] buffer = base64.decodeBuffer(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return  keyFactory.generatePublic(keySpec).getEncoded();
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法");
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空");
        }
    }

    /**
     * 从文件中加载私钥
     *
     * @param path 私钥文件名
     * @return 是否成功
     * @throws Exception
     */
    public static String loadPrivateKeyByFile(String path) throws Exception {
        try {
            BufferedReader br = new BufferedReader(new FileReader(path
                   ));
            String readLine = null;
            StringBuilder sb = new StringBuilder();
            while ((readLine = br.readLine()) != null) {
                if (readLine.charAt(0) == '-') {
                    continue;
                } else {
                    sb.append(readLine);
                    sb.append('\r');
                }
            }
            br.close();
            return sb.toString();
        } catch (IOException e) {
            throw new Exception("私钥数据读取错误");
        } catch (NullPointerException e) {
            throw new Exception("私钥输入流为空");
        }
    }


    public static byte[] loadPrivateKeyByStr(String privateKeyStr) throws Exception
    {
        try {
            BASE64Decoder base64Decoder = new BASE64Decoder();
            byte[] buffer = base64Decoder.decodeBuffer(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return  keyFactory.generatePrivate(keySpec).getEncoded();
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("私钥非法");
        } catch (NullPointerException e) {
            throw new Exception("私钥数据为空");
        }
    }


    /**
     * 加载EC私钥中的私钥
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static String getEcPriverKey(String privateKey) throws Exception{
        byte[] server_sec1 = DatatypeConverter.parseBase64Binary(privateKey);
        ASN1Sequence seq = ASN1Sequence.getInstance(server_sec1);
        org.bouncycastle.asn1.sec.ECPrivateKey pKey = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(seq);
        AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, pKey.getParameters());
        byte[] server_pkcs8 = new PrivateKeyInfo(algId, pKey).getEncoded();
        KeyFactory fact = KeyFactory.getInstance ("EC",new BouncyCastleProvider());
        PrivateKey pkey = fact.generatePrivate (new PKCS8EncodedKeySpec(server_pkcs8));
        System.out.println (pkey.getClass().getName() + " " + pkey.getAlgorithm());
        BCECPrivateKey bcecPrivateKey = (BCECPrivateKey)  pkey;
        return bcecPrivateKey.getD().toString(16);
    }

    /**
     *  加载EC公钥证书中的公钥
     * @param
     * @return
     */
//    public static byte[] getCSPK(String csCertBase64) throws IOException {
//        byte[] csCert = new BASE64Decoder().decodeBuffer(csCertBase64);
//        InputStream inStream = new ByteArrayInputStream(csCert);
//        ASN1Sequence seq = null;
//        ASN1InputStream aIn;
//        try {
//            aIn = new ASN1InputStream(inStream);
//            seq = (ASN1Sequence) aIn.readObject();
//            X509CertificateStructure cert = new X509CertificateStructure(seq);
//            SubjectPublicKeyInfo subjectPublicKeyInfo = cert.getSubjectPublicKeyInfo();
//            DERBitString publicKeyData = subjectPublicKeyInfo.getPublicKeyData();
//            byte[] publicKey = publicKeyData.getEncoded();
//            byte[] encodedPublicKey = publicKey;
//            byte[] eP = new byte[64];
//            System.arraycopy(encodedPublicKey, 4, eP, 0, eP.length);
//            return eP;
//        } catch (Exception e) {
//
//            e.printStackTrace();
//        }
//        return null;
//    }

}
